﻿#include "thread_name.h"

#ifdef WIN32
#include <windows.h>
#else
#include <thread>
#include <sys/prctl.h>
#endif


std::optional<std::string> as::thread::GetThreadName(std::thread::id tid)
{
#ifdef WIN32
	return nullptr;
#else
    char name[16];
    prctl(PR_GET_NAME, name);
	return name;
#endif
}

std::optional<std::string> as::thread::GetCurThreadName()
{
	return nullptr;
}

bool as::thread::SetThreadName(std::thread::id tid, std::string name)
{
#ifdef WIN32
#pragma pack(push, 8)
	struct THREADNAME_INFO {
		DWORD dwType; // Must be 0x1000
		LPCSTR szName; // Pointer to name (in user address space)
		DWORD dwThreadID; // Thread ID (-1 for caller thread)
		DWORD dwFlags; // Reserved for future use; must be zero
	};
	union TNIUnion {
		THREADNAME_INFO tni;
		ULONG_PTR upArray[4];
	};
#pragma pack(pop)

	static constexpr DWORD kMSVCException = 0x406D1388;

	// std::thread::id is a thin wrapper around an integral thread id,
	// so just extract the ID.


	unsigned int id;
	std::memcpy(&id, &tid, sizeof(id));
	TNIUnion tniUnion = { 0x1000, name.data(), id, 0 };

	return [&]() {
		__try
		{
			RaiseException(kMSVCException, 0, 4, tniUnion.upArray);
		}
		__except (GetExceptionCode() == kMSVCException ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_EXECUTE_HANDLER)
		{
			// Swallow the exception when a debugger isn't attached.
		}
		return true;
		}();
#else
	prctl(PR_SET_NAME, name.c_str());
	return true;
#endif
}

uint32_t as::thread::GetThreadId()
{
#ifdef WIN32
	return GetCurrentThreadId();
#else
	return pthread_self();
#endif
}

uint32_t as::thread::ThreadIdToUInt()
{
	auto id = std::this_thread::get_id();
	std::hash<std::thread::id> hasher;
	return static_cast<uint32_t>(hasher(id));
}

